/**
 * Copyright (C) 2011 Denatics Development
 * 
 * Programmed by David Tiersch <dtiersch@denatics.com>
 * 
 * This file is part of B0mberman.
 * 
 * B0mberman is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * B0mberman is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with B0mberman. If not, see <http://www.gnu.org/licenses/>.
 */
package com.denatics.bomberman.engine;

import java.util.List;
import java.util.Observable;
import java.util.Observer;

import com.denatics.bomberman.Config;
import com.denatics.bomberman.control.KeyAction;
import com.denatics.bomberman.control.PlayerControl.Type;
import com.denatics.bomberman.entities.Entity;
import com.denatics.bomberman.sprites.Player;
import com.denatics.bomberman.sprites.Sprite;
import com.denatics.bomberman.tools.Debugger;
import com.denatics.bomberman.tools.Tickable;

/**
 * This class manages all aspects of the game which affect the Gameplay
 * directly.
 * 
 * @author David Tiersch <dtiersch@denatics.com>
 */
public final class Game implements Tickable, GameAPI, Observer {

    /**
     * Maximum number of Players.
     */
    private static final int MAXPLAYER = 4;

    /**
     * Dimensions of the Gamefield.
     */
    private static final int DIMENSION = 2;

    /**
     * Help-Constant with id of Player 3.
     */
    private static final int PLAYER3 = 2;

    /**
     * Two. Every other name would be unintentional.
     */
    private static final int TWO = 2;

    /**
     * Player of the Game.
     */
    private final Player[] player;

    /**
     * Gamefield.
     */
    private final Field field;

    /**
     * Cache for the action being performed of every User.
     */
    private final Type[] currentAction;

    /**
     * Initializes the game.
     * 
     * @param numberOfPlayers
     *            Amount of Players
     */
    public Game(final int numberOfPlayers) {
        int players = numberOfPlayers;
        if (players < 1) {
            players = 1;
        }
        if (players > MAXPLAYER) {
            players = MAXPLAYER;
        }
        this.field = new Field(Config.FIELD_DIM[0], Config.FIELD_DIM[1]);
        this.player = new Player[players];
        this.currentAction = new Type[players];
        for (int i = 0; i < players; i++) {
            this.player[i] = new Player(i);
            int[] startpos = new int[DIMENSION];
            switch (i) {
            case 0:
                // upper left corner
                startpos[0] = 0;
                startpos[1] = 0;
                break;
            case 1:
                // down right corner
                startpos[0] = Config.FIELD_DIM[0] - 1;
                startpos[1] = Config.FIELD_DIM[1] - 1;
                break;
            case PLAYER3:
                // upper right corner
                startpos[0] = Config.FIELD_DIM[0] - 1;
                startpos[1] = 0;
                break;
            default:
                // down left corner
                startpos[0] = 0;
                startpos[1] = Config.FIELD_DIM[1] - 1;
            }
            this.field.getTile(startpos[0], startpos[1]).addSprite(
                    this.player[i]);
        }
        Debugger.printInfo("Gamefield succesful created");
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.denatics.bomberman.Tickable#tick()
     */
    @Override
    public void tick() {
        for (int i = 0; i < this.player.length; i++) {
            if (this.currentAction[i] == null) {
                continue;
            }
            movePlayer(this.player[i], this.currentAction[i]);
        }

        for (int i = 0; i < this.player.length; i++) {
            this.player[i].tick();
        }
    }

    /**
     * Checks if the operation is permitted an executes it.
     * 
     * @param playerfig
     *            Player on whom the Operation should run
     * @param type
     *            Operation to run
     */
    private void movePlayer(final Player playerfig, final Type type) {
        int[] coord = this.field.getCoords(playerfig);
        int[] playerpos = playerfig.getOffset();
        int[] newplayerpos =
                { playerpos[0] + type.getChange()[0],
                        playerpos[1] + type.getChange()[1] };
        if (!field.intersects(playerfig, newplayerpos)) {
            playerpos[0] += type.getChange()[0];
            playerpos[1] += type.getChange()[1];
            // switch Tile if crossed border between 2
            if (playerpos[0] > Config.MOVEMENT_SPEED / TWO) {
                playerpos[0] -= Config.MOVEMENT_SPEED;
                field.getTile(coord[0], coord[1]).removeSprite(playerfig);
                field.getTile(++coord[0], coord[1]).addSprite(playerfig);
            }
            if (playerpos[0] < (-1) * Config.MOVEMENT_SPEED / TWO) {
                playerpos[0] += Config.MOVEMENT_SPEED;
                field.getTile(coord[0], coord[1]).removeSprite(playerfig);
                field.getTile(--coord[0], coord[1]).addSprite(playerfig);
            }
            if (playerpos[1] > Config.MOVEMENT_SPEED / TWO) {
                playerpos[1] -= Config.MOVEMENT_SPEED;
                field.getTile(coord[0], coord[1]).removeSprite(playerfig);
                field.getTile(coord[0], ++coord[1]).addSprite(playerfig);
            }
            if (playerpos[1] < (-1) * Config.MOVEMENT_SPEED / TWO) {
                playerpos[1] += Config.MOVEMENT_SPEED;
                field.getTile(coord[0], coord[1]).removeSprite(playerfig);
                field.getTile(coord[0], --coord[1]).addSprite(playerfig);
            }
            playerfig.setOffset(playerpos[0], playerpos[1]);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.denatics.bomberman.engine.GameAPI#getEntity(int, int)
     */
    @Override
    public Entity getEntity(final int x, final int y) {
        return this.field.getTile(x, y).getEntity();
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.denatics.bomberman.engine.GameAPI#getSprites(int, int)
     */
    @Override
    public List<Sprite> getSprites(final int x, final int y) {
        return this.field.getTile(x, y).getSprites();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    @Override
    public void update(final Observable arg0, final Object arg1) {
        final String s1 = "Player ";
        final String s2 = " Key ";
        final int keyid = ((KeyAction) arg1).getKeyID();
        for (int i = 0; i < this.player.length; i++) {
            Type type = player[i].checkKey(keyid);
            if (type != null) {
                if (((KeyAction) arg1).isPressed()) {
                    this.currentAction[i] = type;
                    Debugger.printDebug(s1 + (i + 1) + s2 + type + "pressed");
                } else if (this.currentAction[i] == type) {
                    this.currentAction[i] = null;
                    Debugger.printDebug(s1 + (i + 1) + s2 + type + "released");
                }
            }
        }
    }
}
